#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include "stdio.h"
#include <errno.h>
#include "cb.h"

#define report_exceptional_condition() abort ()
    void
ring_buffer_create (struct ring_buffer *buffer, unsigned long order)
{
    char path[] = "/dev/shm/ring-buffer-XXXXXX";
    int file_descriptor;
    void *address;
    int status;

    file_descriptor = mkstemp (path);
    if (file_descriptor < 0) {
        printf("Hi, mkstemp() failed!\n");
        report_exceptional_condition ();
    }

    status = unlink (path);
    if (status) {
        printf("Hi, unlink() failed!\n");
        report_exceptional_condition ();
    }

    buffer->count_bytes = 1UL << order;
    buffer->write_offset_bytes = 0;
    buffer->read_offset_bytes = 0;

    status = ftruncate (file_descriptor, buffer->count_bytes);
    if (status) {
        printf("Hi, ftruncate() failed!\n");
        report_exceptional_condition ();
    }

    buffer->address = mmap (NULL, buffer->count_bytes << 1, PROT_NONE,
            MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);

    if (buffer->address == MAP_FAILED) {
        printf("Hi, buffer->address = mmap() failed!\n");
        report_exceptional_condition ();
    }

    address =
        mmap (buffer->address, buffer->count_bytes, PROT_READ | PROT_WRITE,
                MAP_FIXED | MAP_SHARED, file_descriptor, 0);

    if (address != buffer->address) {
        printf("Hi, address = mmap() failed!\n");
        report_exceptional_condition ();
    }

    address = mmap (buffer->address + buffer->count_bytes,
            buffer->count_bytes, PROT_READ | PROT_WRITE,
            MAP_FIXED | MAP_SHARED, file_descriptor, 0);

    if (address != buffer->address + buffer->count_bytes) {
        printf("Hi, new mmap() failed! %d\n", errno);
        printf("Hi, count_bytes(%d) failed!\n", buffer->count_bytes);
        report_exceptional_condition ();
    }

    status = close (file_descriptor);
    if (status) {
        printf("Hi, close() failed!\n");
        report_exceptional_condition ();
    }
}

    void
ring_buffer_free (struct ring_buffer *buffer)
{
    int status;

    status = munmap (buffer->address, buffer->count_bytes << 1);
    if (status) {
        printf("Hi, munmap() failed!\n");
        report_exceptional_condition ();
    }
}

    void *
ring_buffer_write_address (struct ring_buffer *buffer)
{
    return buffer->address + buffer->write_offset_bytes; /*** void pointer arithmetic is a constraint violation. ***/
}

    void
ring_buffer_write_advance (struct ring_buffer *buffer,
        unsigned long count_bytes)
{
    buffer->write_offset_bytes += count_bytes;
}

    void *
ring_buffer_read_address (struct ring_buffer *buffer)
{
    return buffer->address + buffer->read_offset_bytes;
}

    void
ring_buffer_read_advance (struct ring_buffer *buffer,
        unsigned long count_bytes)
{
    buffer->read_offset_bytes += count_bytes;

    if (buffer->read_offset_bytes >= buffer->count_bytes)
    {
        buffer->read_offset_bytes -= buffer->count_bytes;
        buffer->write_offset_bytes -= buffer->count_bytes;
    }
}

    unsigned long
ring_buffer_count_bytes (struct ring_buffer *buffer)
{
    return buffer->write_offset_bytes - buffer->read_offset_bytes;
}

    unsigned long
ring_buffer_count_free_bytes (struct ring_buffer *buffer)
{
    return buffer->count_bytes - ring_buffer_count_bytes (buffer);
}

    void
ring_buffer_clear (struct ring_buffer *buffer)
{
    buffer->write_offset_bytes = 0;
    buffer->read_offset_bytes = 0;
}
